package com.kaihong.template.ble.operation.impl;

import ohos.app.Context;
import ohos.bluetooth.BluetoothHost;
import ohos.bluetooth.BluetoothRemoteDevice;
import ohos.bluetooth.ProfileBase;
import ohos.bluetooth.ble.BleCentralManager;
import ohos.bluetooth.ble.BleCentralManagerCallback;
import ohos.bluetooth.ble.BlePeripheralDevice;
import ohos.bluetooth.ble.BleScanFilter;
import ohos.bluetooth.ble.BleScanResult;
import ohos.event.commonevent.CommonEventManager;
import ohos.event.commonevent.CommonEventSubscribeInfo;
import ohos.event.commonevent.MatchingSkills;
import ohos.rpc.RemoteException;
import ohos.utils.zson.ZSONObject;

import com.kaihong.template.ble.operation.BleCentralProcessor;
import com.kaihong.template.ble.operation.callback.adapter.BluetoothAdapterStateChangeCallback;
import com.kaihong.template.ble.operation.callback.adapter.BluetoothDeviceFoundCallback;
import com.kaihong.template.ble.operation.impl.subscriber.BluetoothAdapterStateSubscriber;
import com.kaihong.template.ble.utils.BluetoothProfileByteUtil;
import com.kaihong.template.opinterface.OperatorCode;
import com.kaihong.template.utils.Logger;

import java.util.*;
import java.util.stream.Collectors;


/**
 * Ble Central Operator
 */
public class BleCentralOperator implements BleCentralProcessor<BlePeripheralDevice> {
    private static final String TAG = "BleCentralOperator";

    private static volatile BleCentralOperator sInstance;

    private final BluetoothHost bluetoothHost;

    private String mMac;

    private final List<UUID> uuidList;

    private final BleCentralManager centralManager;

    private final Map<String, BlePeripheralDevice> scanBleDevicesMap;

    private List<BleScanResult> mScanBleResults;

    private final BluetoothAdapterStateSubscriber bluetoothAdapterStateSubscriber;

    private Context context;

    private BluetoothDeviceFoundCallback bluetoothDeviceFoundCallback;
    private boolean isScanning = false;

    private BleCentralOperator(Context context) {
        this.context = context;
        uuidList = new ArrayList<>();
        scanBleDevicesMap = new HashMap<>();
        mScanBleResults = new ArrayList<>();
        bluetoothHost = BluetoothHost.getDefaultHost(context);
        centralManager = new BleCentralManager(this.context, getCentralManagerCallback());
        bluetoothAdapterStateSubscriber = new BluetoothAdapterStateSubscriber(getStageChangeSubscribeInfo());
        try {
            CommonEventManager.subscribeCommonEvent(bluetoothAdapterStateSubscriber);
        } catch (RemoteException e) {
            Logger.e(TAG, "BleCentralOperator construct exception: " + e.getCause().getLocalizedMessage());
        }
    }

    /**
     * create single instance
     *
     * @param context context
     * @return single instance
     */
    public static BleCentralOperator getInstance(Context context) {
        Logger.i(TAG, "BleCentralOperator getInstance");
        if (sInstance == null) {
            synchronized (BleCentralOperator.class) {
                if (sInstance == null) {
                    sInstance = new BleCentralOperator(context);
                }
            }
        } else {
            sInstance.context = context;
        }
        return sInstance;
    }

    private BleCentralManagerCallback getCentralManagerCallback() {
        return new BleCentralManagerCallback() {
            @Override
            public void scanResultEvent(BleScanResult bleScanResult) {
                if (bleScanResult == null) {
                    return;
                }
                BlePeripheralDevice device = bleScanResult.getPeripheralDevice();
                if (device != null) {
                    Logger.i(TAG, "scan MacAddress:" + device.getDeviceAddr() + "/Rssi:" + bleScanResult.getRssi());
                }
                if (device == null) {
                    return;
                }
                List<UUID> uuids = bleScanResult.getServiceUuids();
                if (uuids != null) {
                    Logger.i(TAG, "UUIDs:" + uuids.toString());
                }
                String deviceInfo = ZSONObject.toZSONString(device);
                String allServiceData = "";
                if (bleScanResult.getServiceData() != null && bleScanResult.getServiceData().values() != null) {
                    List<String> serviceDataList = bleScanResult.getServiceData()
                            .values()
                            .stream()
                            .map(BluetoothProfileByteUtil::bytesToHexString)
                            .collect(Collectors.toList());
                    allServiceData = String.join(",", serviceDataList);
                }
                scanBleDevicesMap.put(device.getDeviceAddr(), device);
                String[] advertiseServiceUuids =
                        bleScanResult.getServiceUuids().stream().map(UUID::toString).toArray(String[]::new);
//                boolean isFilterService = isFilterServices(uuidList, advertiseServiceUuids);
//                if (!isFilterService) {
                if (bluetoothDeviceFoundCallback != null) {
                    bluetoothDeviceFoundCallback.onBluetoothDeviceFound(new String[]{deviceInfo},
                            device.getDeviceAddr(), advertiseServiceUuids, "", allServiceData);
                }
                if (device.getDeviceName().get().startsWith("Hi-nihao")) {
                    Logger.i(TAG, "扫描到mac:" + device.getDeviceAddr() + "/" + device.getDeviceName().get());
                    mScanBleResults.add(bleScanResult);
                }

//                String mac = device.getDeviceAddr();
//                if (mac.equals(mMac)) {
//                    if (isScanning) {
//                        stopBluetoothDevicesDiscovery();
//                    }
//                    BleOperatorImpl.getInstance(context).createBleConnection(mMac);
//                    Logger.i(TAG, "开始连接mac:" + mac + "/" + device.getDeviceName().get());
//                }

//                if (device.getDeviceName().get().startsWith("Hi-nihao")) {
//                    if (isScanning) {
//                        stopBluetoothDevicesDiscovery();
//                    }
//                    String mac = device.getDeviceAddr();
//                    mMac = mac;
//                    BleOperatorImpl.getInstance(context).createBleConnection(mac);
//                    Logger.i(TAG, "开始连接mac:" + mac + "/" + device.getDeviceName().get());
//                }
            }

            @Override
            public void scanFailedEvent(int integer) {
            }

            @Override
            public void groupScanResultsEvent(List<BleScanResult> list) {
            }
        };
    }

    private boolean isFilterServices(List<UUID> uuidList, String[] advertiseServiceUuids) {
        boolean isFilterService = false;
        if (!uuidList.isEmpty()) {
            for (String advertiseServiceUuid : advertiseServiceUuids) {
                if (uuidList.contains(UUID.fromString(advertiseServiceUuid))) {
                    return false;
                } else {
                    isFilterService = true;
                }
            }
        }
        return isFilterService;
    }

    private CommonEventSubscribeInfo getStageChangeSubscribeInfo() {
        MatchingSkills matchingSkills = new MatchingSkills();
        matchingSkills.addEvent(BluetoothHost.EVENT_HOST_STATE_UPDATE);
        matchingSkills.addEvent(BluetoothHost.EVENT_HOST_DISCOVERY_STARTED);
        matchingSkills.addEvent(BluetoothHost.EVENT_HOST_DISCOVERY_FINISHED);
        return new CommonEventSubscribeInfo(matchingSkills);
    }

    @Override
    public int openBluetoothAdapter() {
        boolean isSuccess = bluetoothHost.enableBt();
        return isSuccess ? OperatorCode.ERROR_CODE_OK : OperatorCode.ERROR_CODE_COMMON_ERR;
    }

    @Override
    public int closeBluetoothAdapter() {
        boolean isSuccess = bluetoothHost.disableBt();
        return isSuccess ? OperatorCode.ERROR_CODE_OK : OperatorCode.ERROR_CODE_COMMON_ERR;
    }

    @Override
    public boolean getBluetoothAdapterState() {
        return bluetoothHost.getBtState() == BluetoothHost.STATE_ON;
    }

    @Override
    public void onBluetoothAdapterStateChange(BluetoothAdapterStateChangeCallback stateChangeCallback) {
        Logger.i(TAG, "onBluetoothAdapterStateChange");
        bluetoothAdapterStateSubscriber.setBluetoothAdapterStateChangeCallback(stateChangeCallback);
    }

    @Override
    public int startBluetoothDevicesDiscovery(UUID[] services, String deviceId, boolean isAllowDuplicatesKey, int interval) {
        Logger.i(TAG, "startBluetoothDevicesDiscovery...");
        this.mMac = deviceId;
        uuidList.clear();
        uuidList.addAll(Arrays.asList(services));
        boolean hasConnect = getBleConnectionState(deviceId);
        Logger.e(TAG, "getBleConnectionState is = " + hasConnect);
        if (hasConnect) {
            BleOperatorImpl.getInstance(context).createBleConnection(deviceId);
        } else {
            if (centralManager == null) {
                Logger.e(TAG, "centralManager == null");
                return OperatorCode.ERROR_CODE_COMMON_ERR;
            }
            isScanning = true;
            List<BleScanFilter> emptyList = new ArrayList<>();
            centralManager.startScan(emptyList);
            mScanBleResults = new ArrayList<>();
            Timer timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    List<BleScanResult> scanBleResults = mScanBleResults;
                    Logger.i(TAG, "mScanBleResults size=:" + scanBleResults.size());
                    if (scanBleResults.size() > 0) {
                        scanBleResults.sort((o1, o2) -> o2.getRssi() - o1.getRssi());
                        BlePeripheralDevice device = scanBleResults.get(0).getPeripheralDevice();
                        if (isScanning) {
                            stopBluetoothDevicesDiscovery();
                        }
                        String mac = device.getDeviceAddr();
                        mMac = mac;
                        BleOperatorImpl.getInstance(context).createBleConnection(mac);
                        Logger.i(TAG, "开始连接mac:" + mac + "/" + device.getDeviceName().get());
                        timer.cancel();
                        mScanBleResults.clear();
                    }
                }
            }, 8 * 1000, 5 * 1000);

        }
        return OperatorCode.ERROR_CODE_OK;
    }

    @Override
    public int stopBluetoothDevicesDiscovery() {
        Logger.i(TAG, "Stop Bluetooth Devices Discovery");
        centralManager.stopScan();
        isScanning = false;
        return OperatorCode.ERROR_CODE_OK;
    }

    @Override
    public void onBluetoothDeviceFound(BluetoothDeviceFoundCallback deviceFoundCallback) {
        bluetoothDeviceFoundCallback = deviceFoundCallback;
    }

    @Override
    public String getDeviceId() {
        List<BluetoothRemoteDevice> bleRemoteDeviceList = bluetoothHost.getPairedDevices();
        if (bleRemoteDeviceList != null && !bleRemoteDeviceList.isEmpty()) {
            return bleRemoteDeviceList.get(0).getDeviceAddr();
        }
        return "";
    }

    @Override
    public BlePeripheralDevice getBleGattDevice(String deviceId) {
        return scanBleDevicesMap.get(deviceId);
    }

    @Override
    public boolean getBleConnectionState(String deviceId) {
        List<BlePeripheralDevice> devicesByStates =
                this.centralManager.getDevicesByStates(new int[]{ProfileBase.STATE_CONNECTED});
        if (devicesByStates != null) {
            for (BlePeripheralDevice device : devicesByStates) {
                if (device.getDeviceAddr().equals(deviceId)) {
                    return true;
                }
            }
        }
        return false;
    }
}
